/*
 * @file            include/connection_pool.c
 * @description     
 * @author          diabio <2591674331@qq.com>
 * @createTime      2025-05-06 20:42:57
 * @lastModified    2025-05-18 22:51:04
 * Copyright ©diabio All rights reserved
*/

#include "connection_pool.h"

#include <mysql/mysql.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>

// 函数声明
static int createConnectionWithLock(ConnectionPool_t *connPool, int createNum);
static Connection_t internalEstablishNewDbConnection(DataBase_t dbConfig);

// 创建连接池
ConnectionPool_t *createConnectionPool(DataBase_t db, int connStartNum, int connPoolSize)
{
    ConnectionPool_t *connPool = (ConnectionPool_t *)malloc(sizeof(ConnectionPool_t));
    if (connPool == NULL) {
        fprintf(stderr, "Failed to allocate memory for connection pool\n");
        return NULL;
    }
    connPool->lastError.errorCode = CP_SUCCESS;
    connPool->lastError.errorMsg[0] = '\0';
    connPool->db = db;
    connPool->curConnNum = 0;
    connPool->freeConnNum = 0;
    connPool->initConnNum = connStartNum;
    connPool->connPoolSize = connPoolSize;
    connPool->timeout.timeoutMs = DEFAULT_GET_CONN_TIMEOUT_MS;
    // connPool->connlist = (Connection_t *)malloc(sizeof(Connection_t) * connPoolSize);
    connPool->connlist = calloc(connPoolSize, sizeof(Connection_t)); 
    if (connPool->connlist == NULL) {
        fprintf(stderr, "Failed to allocate memory for connection list\n");
        connPool->lastError.errorCode = CP_ERR_MALLOC;
        snprintf(connPool->lastError.errorMsg, 
            sizeof(connPool->lastError.errorMsg), "Failed to allocate memory for connection list");
        free(connPool);
        return NULL;
    }
    pthread_mutex_init(&connPool->mutex, NULL);
    pthread_cond_init(&connPool->cond, NULL);

    int actuallyCreated = createConnectionWithLock(connPool, connStartNum); // createConnection 返回成功创建的数量
    if (actuallyCreated < connStartNum) { 
        fprintf(stderr, "Failed to create initial connections. Expected %d, created fewer. Check pool lastError.\n", connStartNum);
        if (connPool->lastError.errorCode == CP_SUCCESS && connStartNum > 0) { // 如果 createConnection 未设置错误
            connPool->lastError.errorCode = CP_ERR_INIT_POOL_FAILED;
            snprintf(connPool->lastError.errorMsg, sizeof(connPool->lastError.errorMsg), "Initial connection creation failed");
        }
        // 清理已部分创建的连接
        for (int i = 0; i < connPool->curConnNum; ++i) { // connPool->curConnNum 是实际创建的数量
            if (connPool->connlist[i].conn) {
                mysql_close(connPool->connlist[i].conn);
            }
        }
        free(connPool->connlist);
        pthread_mutex_destroy(&connPool->mutex);
        pthread_cond_destroy(&connPool->cond);
        free(connPool);
        return NULL;
    }

    return connPool;
}

// 获取连接（新增超时机制）
Connection_t *getConnection(ConnectionPool_t *connPool)
{
    if (connPool == NULL) {
        fprintf(stderr, "Error: getConnection called with NULL pool.\n");
        // 如果 connPool 是 NULL，我们不能安全地访问 connPool->lastError
        // 这种情况应该由调用者处理，或者有一个全局的错误记录方式
        return NULL;
    }
    struct timespec timeout;
    if (clock_gettime(CLOCK_REALTIME, &timeout) == -1) {
        perror("clock_gettime failed");
        // 同样，如果 connPool 本身是 NULL，这里会崩溃
        // 假设 connPool 在这里是非 NULL 的
        connPool->lastError.errorCode = CP_ERR_GET_TIME_FAILED; 
        snprintf(connPool->lastError.errorMsg, sizeof(connPool->lastError.errorMsg), "Failed to get current time");
        return NULL;
    }

    long ms_to_add = connPool->timeout.timeoutMs;
    timeout.tv_sec += ms_to_add / 1000;
    timeout.tv_nsec += (ms_to_add % 1000) * 1000000L;

    // 规范化 timespec，防止 tv_nsec 越界
    if (timeout.tv_nsec >= 1000000000L) {
        timeout.tv_sec += timeout.tv_nsec / 1000000000L;
        timeout.tv_nsec %= 1000000000L;
    }

    pthread_mutex_lock(&connPool->mutex);

    // 外层循环，用于在各种操作后重新评估状态或等待
    while (1) {
        // 情况1： 有空闲连接
        if (connPool->freeConnNum > 0) {
            for (int i = 0; i < connPool->curConnNum; i++) {
                if (connPool->connlist[i].isFree) {
                    // 健康检查
                    if (checkConnectionHealth(&connPool->connlist[i]) != CP_SUCCESS) {
                        // 连接不健康，关闭并从池中移除
                        mysql_close(connPool->connlist[i].conn);
                        connPool->connlist[i].conn = NULL;
                        connPool->freeConnNum--; // 一个空闲连接没了

                        // 保持数组紧凑
                        for (int j = i; j < connPool->curConnNum - 1; j++) {
                            connPool->connlist[j] = connPool->connlist[j + 1];
                        }
                        // 清理最后一个（现在重复的）元素槽位
                        if (connPool->curConnNum > 0) { // 避免 connPool->curConnNum - 1 为负
                             // 将最后一个有效元素槽位置空
                            connPool->connlist[connPool->curConnNum - 1].conn = NULL;
                            connPool->connlist[connPool->curConnNum - 1].isFree = 0; // 或其他合适的默认值
                        }
                        connPool->curConnNum--; // 总连接数减少
                        i--; // 因为元素前移了，所以要重新检查当前索引 i
                        // connPool->lastError 应该由 checkConnectionHealth 设置，或者在这里设置一个通用错误
                        // 例如: connPool->lastError.errorCode = CP_ERR_CONN_INVALID;
                        // snprintf(connPool->lastError.errorMsg, sizeof(connPool->lastError.errorMsg), "Found and removed an invalid connection.");
                        continue; // 继续在空闲列表中查找下一个
                    }
                    // 连接健康，分配出去
                    connPool->connlist[i].isFree = 0;
                    connPool->freeConnNum--;
                    connPool->connlist[i].lastUsedTime = time(NULL);
                    pthread_mutex_unlock(&connPool->mutex);
                    return &connPool->connlist[i];
                }
            }
            // 如果循环完了还没拿到（理论上 freeConnNum > 0 应该能拿到，除非健康检查全失败了）
            // 此时 freeConnNum 可能因为移除了坏连接而变了，让循环自然继续到创建或等待逻辑
        }

        // 情况2：没有可用空闲连接（或者所有空闲连接都不健康被移除了），尝试创建新连接
        if (connPool->curConnNum < connPool->connPoolSize) {
            DataBase_t dbConfig = connPool->db; // 复制配置，因为要解锁
            pthread_mutex_unlock(&connPool->mutex); // 解锁执行耗时操作
            Connection_t new_connection_object = internalEstablishNewDbConnection(dbConfig);
            pthread_mutex_lock(&connPool->mutex);   // 重新加锁

            if (new_connection_object.conn != NULL) { // 数据库连接本身创建成功
                // 再次检查池状态，因为在释放锁期间，其他线程可能已经填满了池子
                if (connPool->curConnNum < connPool->connPoolSize) {
                    int targetIndex = -1;
                    // 寻找空槽位，优先使用 curConnNum 指向的位置（如果数组是紧凑的）
                    // 如果 connlist 总是紧凑的，那么 connPool->connlist[connPool->curConnNum] 应该是第一个空位
                    if (connPool->connlist[connPool->curConnNum].conn == NULL) { // 假设 connPoolSize > 0
                        targetIndex = connPool->curConnNum;
                    } else {
                        // 如果 curConnNum 位置已被占用（不符合紧凑假设）或作为备用方案，遍历查找
                        for (int i = 0; i < connPool->connPoolSize; ++i) {
                            if (connPool->connlist[i].conn == NULL) {
                                targetIndex = i;
                                break;
                            }
                        }
                    }

                    if (targetIndex == -1) {
                        // 理论上不应该发生：curConnNum < connPoolSize 但没有找到空槽
                        // 这表示连接池内部状态不一致
                        mysql_close(new_connection_object.conn); // 关闭刚创建的连接，防止泄漏
                        connPool->lastError.errorCode = CP_ERR_INTERNAL; 
                        snprintf(connPool->lastError.errorMsg, sizeof(connPool->lastError.errorMsg),
                                 "Internal pool error: Apparent space (cur=%d, size=%d) but no NULL slot found.",
                                 connPool->curConnNum, connPool->connPoolSize);
                        // 不要返回，让它掉到下面的等待逻辑
                    } else {
                        // 成功找到槽位，将新连接加入池中
                        connPool->connlist[targetIndex] = new_connection_object;
                        connPool->connlist[targetIndex].isFree = 0; // 立即被使用
                        connPool->connlist[targetIndex].lastUsedTime = time(NULL);
                        
                        connPool->curConnNum++; // 实际连接数增加

                        connPool->lastError.errorCode = CP_SUCCESS; // 清除之前的错误
                        connPool->lastError.errorMsg[0] = '\0';

                        pthread_cond_signal(&connPool->cond); // 通知其他可能在等待的线程（虽然这个连接被本线程获取了）
                        pthread_mutex_unlock(&connPool->mutex);
                        return &connPool->connlist[targetIndex];
                    }
                } else {
                    // 池子满了（在我们创建连接的期间被其他线程填满）
                    mysql_close(new_connection_object.conn); // 关闭这个无法放入池中的连接
                    connPool->lastError.errorCode = CP_ERR_POOL_FULL; // 需要定义 CP_ERR_POOL_FULL
                    snprintf(connPool->lastError.errorMsg, sizeof(connPool->lastError.errorMsg),
                             "Pool became full while new connection was being established.");
                    // 不要返回，让它掉到下面的等待逻辑
                }
            } else {
                // 数据库连接创建失败
                connPool->lastError = new_connection_object.error; // 从返回的对象中获取错误信息
                // 不要返回，让它掉到下面的等待逻辑
            }
        }

        // 情况3：需要等待 (池满且无空闲，或无法创建/创建失败/创建后池满)
        // 这时候才调用 pthread_cond_timedwait
        int waitRet = pthread_cond_timedwait(&connPool->cond, &connPool->mutex, &timeout);
        if (waitRet == ETIMEDOUT) {
            connPool->lastError.errorCode = ETIMEDOUT;
            snprintf(connPool->lastError.errorMsg, sizeof(connPool->lastError.errorMsg),
                     "Get connection timed out after %ldms while waiting", ms_to_add); // 使用保存的ms值
            pthread_mutex_unlock(&connPool->mutex);
            return NULL;
        } else if (waitRet != 0) {
            connPool->lastError.errorCode = waitRet; // POSIX错误码
            snprintf(connPool->lastError.errorMsg, sizeof(connPool->lastError.errorMsg),
                     "pthread_cond_timedwait failed with error code: %d", waitRet);
            pthread_mutex_unlock(&connPool->mutex);
            return NULL;
        }
        // 如果 waitRet == 0 (被唤醒或伪唤醒), 循环会回到开始，重新检查所有条件
    } // 结束 while(1)

    // 理论上，如果逻辑正确，不应该执行到这里。所有退出路径都应该在循环内部处理。
    // 但为安全起见，如果意外跳出循环，确保解锁。
    pthread_mutex_unlock(&connPool->mutex); // 这一行在当前逻辑下应该是不可达的
    return NULL; // 同样不可达
}

// 销毁连接池(0是正常返回)
int destroyConnectionPool(ConnectionPool_t *connPool)
{
    if (!connPool)
        return 0;  // 空指针直接返回
    int totallNum = connPool->curConnNum;
    int destroyedNum = 0;
    for (int i = 0; i < connPool->curConnNum; i++) {
        if (connPool->connlist[i].conn) {
            mysql_close(connPool->connlist[i].conn);
            connPool->connlist[i].conn = NULL;  // 置空防止野指针
            destroyedNum++;
        }
    }
    free(connPool->connlist);
    int mutexRet = pthread_mutex_destroy(&connPool->mutex);
    if (mutexRet != 0) {
        snprintf(connPool->lastError.errorMsg, sizeof(connPool->lastError.errorMsg), "Destroy mutex failed: %d",
                 mutexRet);
        connPool->lastError.errorCode = mutexRet;
    }
    int condRet = pthread_cond_destroy(&connPool->cond);
    if (condRet != 0) {
        snprintf(connPool->lastError.errorMsg, sizeof(connPool->lastError.errorMsg), "Destroy cond failed: %d",
                 condRet);
        connPool->lastError.errorCode = condRet;
    }
    free(connPool);
    return totallNum == destroyedNum ? CP_SUCCESS : CP_ERR_DESTROY_INCOMPLETE;
}

// 创建数据库连接（优化错误处理） 注：调用此函数前记得先解锁
static int createConnectionWithLock(ConnectionPool_t *connPool, int createNum)
{
    pthread_mutex_lock(&connPool->mutex);
    int successNum = 0;
    time_t now = time(NULL);  // 获取当前时间

    int numToCreate = createNum;
    if (connPool->curConnNum + numToCreate > connPool->connPoolSize) {
        numToCreate = connPool->connPoolSize - connPool->curConnNum;
    }
    if (numToCreate <= 0) { // 如果不需要创建或不能创建
        pthread_mutex_unlock(&connPool->mutex);
        return 0;
    }

    for(int k = 0; k < numToCreate; k++) {
        int targetIndex = connPool->curConnNum; // 新连接将放在这个索引

        connPool->connlist[targetIndex].conn = mysql_init(NULL);
        if (connPool->connlist[targetIndex].conn == NULL) {
            // mysql_init 失败，记录错误
            connPool->lastError.errorCode = CP_ERR_MYSQL_INIT_FAILED;
            snprintf(connPool->lastError.errorMsg, 
                    sizeof(connPool->lastError.errorMsg), "mysql_init failed");
            connPool->connlist[targetIndex].error.errorCode = CP_ERR_MYSQL_INIT_FAILED; 
            continue; 
        }

        if(!mysql_real_connect(connPool->connlist[targetIndex].conn,
                             connPool->db.host, 
                             connPool->db.user, 
                             connPool->db.password, 
                             connPool->db.dbName, 
                             connPool->db.port, 
                             NULL, 0)) {
            connPool->connlist[targetIndex].error.errorCode = mysql_errno(connPool->connlist[targetIndex].conn);
            snprintf(connPool->connlist[targetIndex].error.errorMsg, sizeof(connPool->connlist[targetIndex].error.errorMsg), 
                    "Connect failed: %s", mysql_error(connPool->connlist[targetIndex].conn));
            
            connPool->lastError.errorCode = CP_ERR_MYSQL_CONNECT_FAILED;
            snprintf(connPool->lastError.errorMsg, sizeof(connPool->lastError.errorMsg), 
                    "mysql_real_connect failed for a new connection: %s", mysql_error(connPool->connlist[targetIndex].conn));
    
            mysql_close(connPool->connlist[targetIndex].conn);
            connPool->connlist[targetIndex].conn = NULL; // 确保失败的连接指针为空
            continue;
        }
        connPool->connlist[targetIndex].createTime = now;
        connPool->connlist[targetIndex].lastUsedTime = now;
        connPool->connlist[targetIndex].isFree = 1; // 新创建的连接是空闲的
        connPool->connlist[targetIndex].error.errorCode = 0; // 清除之前的错误信息
        connPool->connlist[targetIndex].error.errorMsg[0] = '\0';

        connPool->curConnNum++;    
        connPool->freeConnNum++;   
        successNum++;
    }
    pthread_mutex_unlock(&connPool->mutex);
    return successNum; 
}



// 释放数据库连接（修正死锁+逻辑错误）
int releaseConnection(ConnectionPool_t *connPool, Connection_t *conn)
{
    if (!conn || !connPool)
        return 0;  // 空指针检查

    pthread_mutex_lock(&connPool->mutex);
    int inConnPool = 0;
    for (int i = 0; i < connPool->curConnNum; i++) {
        // 这里将conn->conn == connPool->connlist[i].conn 替换成
        // conn == &connPool->connlist[i] 
        // 防止万一那个 conn->conn 指向的 MYSQL 对象已经被关闭释放了，然后那块内存又被分配给了池子里的另一个新连接
        if (conn == &connPool->connlist[i] ) {
            inConnPool = 1;
            break;
        }
    }
    if (inConnPool == 0) {
        // 该连接不属于这个池子，错误处理
        connPool->lastError.errorCode = CP_ERR_CONN_NOT_IN_POOL;
        snprintf(connPool->lastError.errorMsg, 
            sizeof(connPool->lastError.errorMsg), "Attempted to release a connection not belonging to this pool");
        pthread_mutex_unlock(&connPool->mutex);
        return CP_ERR_CONN_NOT_IN_POOL;
    }
    conn->isFree = 1;                 // 标记为空闲
    conn->lastUsedTime = time(NULL);  // 更新最后使用时间
    connPool->freeConnNum++;
    // 唤醒一个可能在等待的线程
    pthread_cond_signal(&connPool->cond);
    pthread_mutex_unlock(&connPool->mutex);  // 只解一次锁！
    return CP_SUCCESS;
}

// 清理空闲连接
void poolCleanup(ConnectionPool_t *connPool)
{
    if (!connPool)
        return;

    pthread_mutex_lock(&connPool->mutex);
    time_t now = time(NULL);
    for (int i = 0; i < connPool->curConnNum; i++) {
        // 保持线程数量在初始连接数量之上
        if(connPool->curConnNum <= connPool->initConnNum) {
            break;
        }
        if (connPool->connlist[i].isFree &&
            (now - connPool->connlist[i].lastUsedTime) > IDLE_CONN_CLEANUP_SEC) {  // 5分钟无使用则清理
            mysql_close(connPool->connlist[i].conn);
            connPool->connlist[i].conn = NULL;
            
            int old_cur_conn_num = connPool->curConnNum; // 保存一下移动前的数量

            // 把后面的连接前移填补空位
            for (int j = i; j < connPool->curConnNum - 1; j++) {
                connPool->connlist[j] = connPool->connlist[j + 1];
            }
            connPool->curConnNum--;
            connPool->freeConnNum--;

            // 清理最后一个（现在逻辑上无效的）
            if (old_cur_conn_num > 0 && connPool->curConnNum < old_cur_conn_num) { // 确保确实移除了元素
                connPool->connlist[connPool->curConnNum].conn = NULL; // 使用更新后的 curConnNum 作为索引
                connPool->connlist[connPool->curConnNum].isFree = 0;
                // 也可以考虑重置 Connection_t 的其他字段，比如错误信息和时间戳
                connPool->connlist[connPool->curConnNum].error.errorCode = CP_SUCCESS;
                connPool->connlist[connPool->curConnNum].error.errorMsg[0] = '\0';
                connPool->connlist[connPool->curConnNum].createTime = 0;
                connPool->connlist[connPool->curConnNum].lastUsedTime = 0;
           }
            i--;  // 避免跳过下一个元素
        }
    }
    pthread_mutex_unlock(&connPool->mutex);
}

int checkConnectionHealth(Connection_t *conn) {
    if (!conn || !conn->conn) {
        // 如果 conn 本身不是 NULL，可以尝试设置其错误信息。
        if (conn) {
            conn->error.errorCode = CP_ERR_CONN_INVALID_STRUCT;
            snprintf(conn->error.errorMsg, sizeof(conn->error.errorMsg), "Connection object or MySQL handle is NULL.");
        }
        return CP_ERR_CONN_INVALID_STRUCT; 
    }
    // 方法1 ： 检查连接是否有效
    if (mysql_ping(conn->conn) != 0) {
        // conn->error.errorCode = CP_ERR_MYSQL_CONNECT_FAILED;
        snprintf(conn->error.errorMsg, sizeof(conn->error.errorMsg),
                "Connection ping failed: %s", mysql_error(conn->conn));
        conn->error.errorCode = mysql_errno(conn->conn);
        return CP_ERR_MYSQL_CONNECT_FAILED; // ping失败，认为连接无效
    }
    return CP_SUCCESS; // 连接有效
}

static Connection_t internalEstablishNewDbConnection(DataBase_t dbConfig) {
    Connection_t new_conn_obj;
    new_conn_obj.conn = NULL;
    new_conn_obj.isFree = 0; // 初始可以标记为未使用，或根据逻辑调整
    new_conn_obj.createTime = time(NULL);
    new_conn_obj.lastUsedTime = new_conn_obj.createTime;
    new_conn_obj.error.errorCode = CP_SUCCESS;
    new_conn_obj.error.errorMsg[0] = '\0';

    MYSQL *mysql = mysql_init(NULL);
    if (!mysql) {
        new_conn_obj.error.errorCode = CP_ERR_MYSQL_INIT_FAILED;
        snprintf(new_conn_obj.error.errorMsg, sizeof(new_conn_obj.error.errorMsg), "mysql_init failed");
        return new_conn_obj; // 返回包含错误信息的对象
    }

    if (!mysql_real_connect(mysql, dbConfig.host, dbConfig.user, dbConfig.password,
        dbConfig.dbName, dbConfig.port, NULL, 0)) {
        new_conn_obj.error.errorCode = mysql_errno(mysql); // 使用实际的MySQL错误码
        snprintf(new_conn_obj.error.errorMsg, sizeof(new_conn_obj.error.errorMsg),
                 "mysql_real_connect failed: %s", mysql_error(mysql));
        mysql_close(mysql); // 清理失败的连接尝试
    } else {
        new_conn_obj.conn = mysql; // 连接成功
        // new_conn_obj.isFree = 1; // 如果希望创建后是空闲的（通常getConnection是希望直接用）
    }
    return new_conn_obj;
}